home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / NeXT-Icons / next-icon@gun.com / Apps / ImagePortfolio / Portfolio.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-03  |  29.0 KB  |  1,027 lines

  1. // -------------------------------------------------------------------------------------
  2. // Portfolio.cxx - image Palette instance manager
  3. // -------------------------------------------------------------------------------------
  4. // Permission is granted to freely redistribute this source code, and to use fragments
  5. // of this code in your own applications if you find them to be useful.  This class,
  6. // along with the source code, come with no warranty of any kind, and the user assumes
  7. // all responsibility for its use.
  8. // -------------------------------------------------------------------------------------
  9.  
  10. extern "Objective-C" {
  11. #import <objc/objc.h>
  12. #import <appkit/appkit.h>
  13. #import <libc.h>
  14. #import <stdlib.h>
  15. #import <stdio.h>
  16. #import <string.h>
  17. #import <math.h>
  18. #import    <sys/types.h>
  19. #import    <sys/stat.h>
  20. #import    <sys/time.h>
  21. #import <defaults/defaults.h>
  22. #import <dpsclient/psops.h>
  23. #import <dpsclient/dpsNeXT.h>
  24. #import "fileUtils.h"
  25. #import "threadStdio.h"
  26. }
  27.  
  28. #import "ExpandImage.h"
  29. #import "PaletteCell.h"
  30. #import "PaletteMatrix.h"
  31. #import "Portfolio.h"
  32. #import "ImagePortfolio.h"
  33.  
  34. // -------------------------------------------------------------------------------------
  35.  
  36. /* static image ids */
  37. static id                openFolder   = (id)nil;
  38. static id                closedFolder = (id)nil;
  39. static id                multiFile    = (id)nil;
  40. static id                stopIcon     = (id)nil;
  41.  
  42. /* list of instantiated windows */
  43. static id                instanceList = (id)nil;
  44.  
  45. /* main window location scale */
  46. static NXPoint            winPosScale  = { 0.20, 0.85 };
  47. static NXSize            winOffset = { 64.0, -24.0 };
  48.  
  49. // -------------------------------------------------------------------------------------
  50. // static selectors
  51. extern "Objective-C" {
  52.     static SEL sel_sizeToCells = @selector(sizeToCells);
  53.     static SEL sel_setDocEdited = @selector(setDocEdited:);
  54.     static SEL sel_loadComplete = @selector(_loadComplete);
  55.     static SEL sel_showLargeImage = @selector(showLargeImage:);
  56.     static SEL sel_resignActivePortfolio = @selector(resignActivePortfolio:);
  57.     static SEL sel_doLoad = @selector(_doLoad:);
  58.     static SEL sel_performClose = @selector(performClose:);
  59.     static SEL sel_dirList = @selector(_loadParseString:fromFile:);
  60.     static SEL sel_portfolioList = @selector(_loadParseString:fromPortfolio:);
  61. }
  62.  
  63. // -------------------------------------------------------------------------------------
  64. // misc defines
  65. #define    isSHIFT(S)            ((([NXApp currentEvent]->flags) & (S))? YES : NO)
  66. #define    isLOADING            ([iconButtonId altImage] == stopIcon)
  67. #define    dftCellROWS            1
  68. #define    dftCellCOLUMNS        5
  69. #define dftFormatNAME        "PortfolioFormat"
  70. #define    X                    origin.x
  71. #define    Y                    origin.y
  72. #define    W                    size.width
  73. #define    H                    size.height
  74.  
  75. // -------------------------------------------------------------------------------------
  76. // rint()
  77. #ifdef rint_bug_fixed
  78. #define RINT(X)                rint(X)
  79. #else
  80. inline double RINT(double x) { return copysign(floor(copysign(x,(double)1.0)+0.5),x); }
  81. #endif rint_bug_fixed
  82.  
  83. // -------------------------------------------------------------------------------------
  84. // PasteBoard dragging operation variables
  85. extern BOOL                    pbDragFilesOk;
  86. extern NXAtom                pbTypes[];
  87. extern int                    pbNumTypes;
  88. extern int                    pbChangeCount;
  89.  
  90. // -------------------------------------------------------------------------------------
  91. @implementation Portfolio
  92.  
  93. // -------------------------------------------------------------------------------------
  94. // internal support
  95.  
  96. /* return first instance */
  97. + firstInstance
  98. {
  99.     return (instanceList && [instanceList count])? [instanceList objectAt:0]: (id)nil;
  100. }
  101.  
  102. // -------------------------------------------------------------------------------------
  103. // internal support
  104.  
  105. /* (re)adjust window size to fit cell size */
  106. - _adjustPreference:(char*)prefBuff
  107. {
  108.     int        rows, cols;
  109.     NXSize    winSize, *cellSize, *gap;
  110.     NXRect    fRect;
  111.     BOOL        rtn;
  112.  
  113.     /* check for allowable preference adjustment */
  114.     if (!adjustPreferences) return (id)nil;
  115.  
  116.     /* free preference buffer and check for successful */
  117.     rtn = [iconMatrix setPreferences:prefBuff returnRows:&rows cols:&cols];
  118.     if (!rtn) return self;
  119.   
  120.     /* calculate minimum window size */
  121.     cellSize = [iconMatrix cellSize];
  122.     gap = [iconMatrix intercell];
  123.     [self getWindowSize:&winSize forCellSize:cellSize gap:gap rows:rows cols:cols];
  124.     [paletteWindow getFrame:&fRect];
  125.     fRect.Y += fRect.H - winSize.height;
  126.     fRect.size = winSize;
  127.   
  128.     /* resize and re-display window */
  129.     [paletteWindow placeWindow:&fRect];
  130.     [self windowDidResize:paletteWindow];
  131.     [paletteWindow display];
  132.     [paletteWindow makeKeyAndOrderFront:(id)nil];
  133.  
  134.     return self;
  135. }
  136.  
  137. /* (main thread only) return file type */
  138. - (int)_fileType:(const char*)fileName
  139. {
  140.     if (XFileIsDirectory(fileName)) return 2;
  141.     if (XFileExists(fileName)) return 1;
  142.     return 0;
  143. }
  144.  
  145. /* (main thread only) load ParseString from file/directory */
  146. - _loadParseString:(ParseString*)ps fromFile:(const char*)dirName
  147. {
  148.     *ps <<= dirName;
  149.     return self;
  150. }
  151.  
  152. /* (main thread only) load ParseString from portfolio doc */
  153. - _loadParseString:(ParseString*)ps fromPortfolio:(const char*)fileName
  154. {
  155.     char    name[MAXPATHLEN + 1];
  156.     FILE    *fNum;
  157.   
  158.     /* return if no file name, or if file can't be openned */
  159.     if (!fileName || !(fNum=fopen(fileName,"r"))) return (id)nil;
  160.   
  161.     /* read image paths from file */
  162.     for (*name = 0; fgets(name, sizeof(name) - 1, fNum);) {
  163.         if (*name == ':') [self _adjustPreference:name + 1];
  164.         else *ps += name;
  165.     }
  166.     fclose(fNum);
  167.     
  168.     return self;
  169. }
  170.  
  171. /* set icon button image */
  172. - _setFileIcon:imageId
  173. {
  174.     if (imageId) [iconButtonId setAltImage:imageId];
  175.     [iconButtonId setImage:[iconButtonId altImage]];
  176.     return self;
  177. }
  178.  
  179. /* set window title from file name */
  180. - _setWindowTitle:(char*)fileName
  181. {
  182.  
  183.     /* set last file path */
  184.     if (fileName) {
  185.         char path[MAXPATHLEN + 1], *p;
  186.         strcpy(path, fileName);
  187.         if (p = rindex(path, '/')) *p = 0; else *path = 0;
  188.         [NXApp setLastPath:path];
  189.         [paletteWindow setTitleAsFilename:fileName];
  190.     } else {
  191.         char name[MAXPATHLEN + 1];
  192.         int len = strlen(strcpy(name, [NXApp lastPath]));
  193.         strcat(name, ((!len||(name[len-1]!='/'))?"/UNTITLED":"UNTITLED"));
  194.         [paletteWindow setTitleAsFilename:name];
  195.     }
  196.  
  197.     return self;
  198. }
  199.  
  200. /* make save panel */
  201. - _savePanel:(char*)title :(char*)extn
  202. {
  203.     id    savePanel = [SavePanel new];
  204.     [savePanel setTitle:title];
  205.     [savePanel setPrompt:"File:"];
  206.     [savePanel setRequiredFileType:extn];
  207.     [savePanel setDirectory:[NXApp lastPath]];
  208.     return savePanel;
  209. }
  210.  
  211. /* (load thread) recursive add directory to image list */
  212. - _addDirectory:(char*)dirPath :(BOOL)chkExtn
  213. {
  214.     if (abortLoad) return (id)nil;
  215.     switch ((int)[self mainThreadPerform:@selector(_fileType:) with:(id)dirPath wait:YES]) {
  216.         case 2:    // directory
  217.             {
  218.                 ParseString fp;
  219.                 [self mainThreadPerform:sel_dirList with:(id)&fp with:(id)dirPath wait:YES];
  220.                 for (int i = 0; !abortLoad && fp[i]; i++) {
  221.                     char filePath[MAXPATHLEN + 1];
  222.                     sprintf(filePath, "%s/%s", dirPath, fp[i]);
  223.                     [self _addDirectory:filePath :chkExtn];
  224.                 }
  225.             }
  226.             return self;
  227.         case 1:    // file
  228.             {
  229.                 if (chkExtn && ![PaletteCell validExtension:dirPath]) return (id)nil;
  230.                 [iconMatrix addImage:dirPath];
  231.             }
  232.             return self;
  233.     }
  234.     return (id)nil;
  235. }
  236.  
  237. /* (load thread) load from doc file */
  238. - _loadDocument:(char*)fileName :(BOOL)checkExtension
  239. {
  240.     ParseString    fp;
  241.     [self mainThreadPerform:sel_portfolioList with:(id)&fp with:(id)fileName wait:YES];
  242.     for (int i = 0; !abortLoad && fp[i]; i++) [self _addDirectory:fp[i] :checkExtension];
  243.     [iconMatrix mainThreadPerform:sel_sizeToCells wait:NO];
  244.     return self;
  245. }
  246.  
  247. /* (load thread) load file list */
  248. - _doLoad:(fileLIST*)fileList
  249. {
  250.     fileLIST    *fl, *next;
  251.   
  252.     /* return if no list */
  253.     if (!fileList) return self;
  254.   
  255.     /* initial load lock */
  256.     for (fl = fileList; fl;) {
  257.  
  258.         /* continue loading if not stopped */
  259.         if (!abortLoad) {
  260.             int i;
  261.             BOOL quit, chkExt, setEdit;
  262.     
  263.             /* load list of images */
  264.             chkExt  = (fl->flags & 2)?YES:NO;
  265.             setEdit = (fl->flags & 1)?NO:YES;
  266.             for (quit = NO, i = 0; !abortLoad && !quit && (fl->list[i]); i++) {
  267.                 char *fp = fl->list[i];
  268.                 if (!strcmp(XFileExtension(fp)+1,docEXTENSION)) [self _loadDocument:fp:NO];
  269.                 else [self _addDirectory:fp:chkExt];
  270.             }
  271.  
  272.             /* set window edited icon (setEdit only true on the first fileLIST loaded) */
  273.             [paletteWindow mainThreadPerform:sel_setDocEdited
  274.                 with:(id)((int)setEdit) wait:NO];
  275.  
  276.         }
  277.     
  278.         /* free list and move to next list of images */
  279.         mutex_lock(loadMutex);
  280.         next = fl->next;
  281.         delete fl;
  282.         fl = next;
  283.         if (!fl) loadList = (fileLIST*)nil;
  284.         adjustPreferences = NO;        // preference adjustment allowed on 1st list only
  285.         mutex_unlock(loadMutex);
  286.     
  287.       }
  288.   
  289.     /* loading complete */
  290.     [self mainThreadPerform:sel_loadComplete wait:NO];
  291.     return self;
  292.     
  293. }
  294.  
  295. /* invoked by load thread when loading is complete */
  296. - _loadComplete
  297. {
  298.     mutex_lock(loadMutex);
  299.     if (!loadList) {            // make sure another load hasn't been requested
  300.         abortLoad = YES;
  301.         [self _setFileIcon:closedFolder];
  302.         [iconMatrix resizeAndDisplay];
  303.     }
  304.     mutex_unlock(loadMutex);
  305.     return self;
  306. }
  307.  
  308. // -------------------------------------------------------------------------------------
  309. // create a new instance
  310.  
  311. /* general initialization */
  312. - init
  313. {
  314.     return [self initFromList:(ParseString*)nil];
  315. }
  316.  
  317. /* initialize and open file name */
  318. - initFromList:(const ParseString*)fileNames
  319. {
  320.     return [self initFromList:fileNames registerWindow:YES];
  321. }
  322.  
  323. /* initialize and open file name */
  324. - initFromList:(const ParseString*)fileNames registerWindow:(BOOL)regWindow
  325. {
  326.     id            docView;
  327.     int            rows, cols;
  328.     const char    *dftFmt;
  329.     NXSize        cellSize, scrnSize, gap, clipSize;
  330.     NXRect        docFrame, winRect;
  331.     static int    winCount = 0;
  332.  
  333.     /* initialize static vars */
  334.     if (!openFolder  ) openFolder   = [NXImage findImageNamed:"openFolder"  ];
  335.     if (!closedFolder) closedFolder = [NXImage findImageNamed:"closedFolder"];
  336.     if (!multiFile   ) multiFile    = [NXImage findImageNamed:"multiFile"   ];
  337.     if (!stopIcon    ) stopIcon     = [NXImage findImageNamed:"stop"        ];
  338.     if (!instanceList) instanceList = listALLOC(1);
  339.  
  340.     /* init super and add myself to the list of active instances */
  341.     [super init];
  342.     [instanceList addObject:self];
  343.     [self resignActivePortfolio:self];
  344.  
  345.     /* load nib */
  346.     [NXApp loadNibSection:"Portfolio.nib" owner:self];
  347.     
  348.     /* clear expanded image panel */
  349.     expandImage = (id)nil;
  350.  
  351.     /* set icon button to send message on mouse down */
  352.     fileIcon = (id)nil;
  353.     [iconButtonId setType:NX_MOMENTARYPUSH];
  354.     [iconButtonId setIconPosition:NX_ICONONLY];
  355.     [iconButtonId sendActionOn:NX_MOUSEDOWNMASK];
  356.     [[iconButtonId setTarget:self] setAction:@selector(iconDrag:)];
  357.     [self _setFileIcon:closedFolder];
  358.   
  359.     /* file name load list flags */
  360.     abortLoad = YES;
  361.     loadMutex = mutex_alloc();
  362.     loadList  = (fileLIST*)nil;
  363.  
  364.     /* make a new matrix */
  365.     docView = [paletteScroll docView];
  366.     [docView getFrame:&docFrame];
  367.     [docView getCellSize:&cellSize];
  368.     iconMatrix = [[PaletteMatrix alloc] initFrame:&docFrame];
  369.     [iconMatrix setCellSize:&cellSize];
  370.     [iconMatrix setFont:[[docView cellAt:0:0] font]];
  371.     [iconMatrix setDelegate:self];
  372.     [iconMatrix setTarget:self];
  373.     [iconMatrix setDoubleAction:sel_showLargeImage];
  374.     [iconMatrix setSelectionByRect:YES];
  375.     [paletteScroll setDocView:iconMatrix];
  376.     [paletteScroll setPageScroll:0.0];                    // full page scrolling
  377.     [[paletteScroll setHorizScroller:(id)nil] free];    // remove the horiz scroller
  378.     [docView free];                                        // free the old view
  379.  
  380.     /* defaults */
  381.     dftFmt = NXReadDefault([NXApp appName], dftFormatNAME);
  382.     if (![iconMatrix setPreferences:(char*)dftFmt returnRows:&rows cols:&cols]) {
  383.         rows = dftCellROWS;
  384.         cols = dftCellCOLUMNS;
  385.     }
  386.  
  387.     /* calculate new window size and location */
  388.     cellSize = *[iconMatrix cellSize];            // re-read cellSize
  389.     gap = *[iconMatrix intercell];
  390.     [NXApp getScreenSize:&scrnSize];
  391.     [self getWindowSize:&winRect.size forCellSize:&cellSize gap:&gap rows:rows cols:cols];
  392.     winRect.X = floor(scrnSize.width *winPosScale.x)+((float)winCount*winOffset.width );
  393.     winRect.Y = floor(scrnSize.height*winPosScale.y)+((float)winCount*winOffset.height);
  394.     if (winRect.X > (scrnSize.width  - 64.0)) winRect.X = scrnSize.width  - 64.0;
  395.     if (winRect.Y > (scrnSize.height - 24.0)) winRect.Y = scrnSize.height - 24.0;
  396.     winRect.Y -= winRect.H;
  397.     winCount = (++winCount) % 5;
  398.  
  399.     /* reposition window */
  400.     [paletteWindow placeWindow:&winRect];
  401.     [self windowDidResize:paletteWindow];
  402.     [paletteScroll getContentSize:&clipSize];
  403.     windowOverhead.width  = winRect.W - clipSize.width ;
  404.     windowOverhead.height = winRect.H - clipSize.height;
  405.   
  406.     /* clear window header statistics */
  407.     [self cellResignedSelected:(id)nil];
  408.  
  409.     /* register window */
  410.     isRegistered = NO;
  411.     allowDrop = regWindow;
  412.     [self _registerWindow];
  413.     [paletteWindow setDelegate:self];
  414.     [paletteWindow setMiniwindowIcon:"doc"];
  415.     [paletteWindow addToEventMask:NX_MOUSEENTEREDMASK];
  416.   
  417.     /* calculate absolute minimum window size */
  418.     minCellSize.width  = 70.0;
  419.     minCellSize.height = 40.0;
  420.     [self getWindowSize:&minWindowSize forCellSize:&minCellSize gap:&gap rows:1 cols:1];
  421.   
  422.     /* load file if specified */
  423.     adjustPreferences = YES;
  424.      sourceFile = (char*)nil;
  425.     if (((void*)fileNames) && (*fileNames)[0]) {
  426.         BOOL isDoc = ((fileNames->count() > 1) ||
  427.             strcmp(XFileExtension((*fileNames)[0]), dotDocEXTENSION))? NO : YES;
  428.         if (isDoc) sourceFile = NXCopyStringBuffer((*fileNames)[0]);
  429.         [self loadFileList:fileNames :isDoc:!isDoc]; 
  430.     }
  431.     [self _setWindowTitle:sourceFile];
  432.  
  433.     /* make window key */
  434.     [paletteWindow display];
  435.     [paletteWindow makeKeyAndOrderFront:(id)nil];
  436.  
  437.     return self;
  438. }
  439.  
  440. /* free image Palette window manager */
  441. - free
  442. {
  443.     [paletteWindow free];
  444.     if (expandImage) { [expandImage free]; expandImage = (id)nil; }
  445.     if (fileIcon) { [fileIcon free]; fileIcon = (id)nil; }
  446.     if (sourceFile) { free(sourceFile); sourceFile = (char*)nil; }
  447.     mutex_free(loadMutex);
  448.     return self;
  449. }
  450.  
  451. // -------------------------------------------------------------------------------------
  452. // active portfolio delegate
  453.  
  454. /* become active portfolio */
  455. - becomeActivePortfolio:sender
  456. {
  457.     isActivePortfolio = YES;
  458.     [paletteWindow becomeMainWindow];
  459.     return self;
  460. }
  461.  
  462. /* resign active portfolio */
  463. - resignActivePortfolio:sender
  464. {
  465.     isActivePortfolio = NO;
  466.     return self;
  467. }
  468.  
  469. /* return true if active */
  470. - (BOOL)isActivePortfolio
  471. {
  472.     return isActivePortfolio;
  473. }
  474.   
  475. /* make portfolio active */
  476. + makeActivePortfolio:(Portfolio*)portObj
  477. {
  478.     [instanceList makeObjectsPerform:sel_resignActivePortfolio with:(id)nil];
  479.     [portObj becomeActivePortfolio:(id)nil];
  480.     return self;
  481. }
  482.  
  483. /* return active portfolio */
  484. + activePortfolio
  485. {
  486.     id    pObj = (id)nil;
  487.     int    i = [instanceList count];
  488.     while(i) if ([(pObj = [instanceList objectAt:--i]) isActivePortfolio]) break;
  489.     return pObj;
  490. }
  491.  
  492. // -------------------------------------------------------------------------------------
  493. // document status
  494.  
  495. /* return true if there are any unsaved files */
  496. + (BOOL)isDocEdited
  497. {
  498.     int    i = [instanceList count];
  499.     while(i) if ([[instanceList objectAt:--i] isDocEdited]) return YES;
  500.     return NO;
  501. }
  502.  
  503. /* return true if this document has been edited */
  504. - (BOOL)isDocEdited
  505. {
  506.     return [paletteWindow isDocEdited];
  507. }
  508.  
  509. /* return true if loading new images (main thread only!) */
  510. - (BOOL)isLoading
  511. {
  512.     return isLOADING;
  513. }
  514.  
  515. // -------------------------------------------------------------------------------------
  516. // font
  517.  
  518. /* return matrix font */
  519. - font
  520. {
  521.     return [iconMatrix font];
  522. }
  523.  
  524. /* change font */
  525. - setFont:fontObj
  526. {
  527.     [iconMatrix setFont:fontObj];
  528.     return self;
  529. }
  530.  
  531. // -------------------------------------------------------------------------------------
  532. // button methods
  533.  
  534. /* show Palette window */
  535. - show:sender
  536. {
  537.     [paletteWindow display];
  538.     [paletteWindow makeKeyAndOrderFront:(id)nil];
  539.     return self;
  540. }
  541.  
  542. /* open file */
  543. - open:sender
  544. {
  545.     return self;
  546. }
  547.   
  548. /* save file names (called by first responder) */
  549. - save:sender
  550. {
  551.   
  552.     /* get file name to save */
  553.     if (!sourceFile) {
  554.         const char *temp;
  555.         id pSave = [self _savePanel:"Save Image Palette" :docEXTENSION];
  556.         if (![pSave runModalForDirectory:[NXApp lastPath] file:""]) return self;
  557.         if (!(temp=[pSave filename])) return self;
  558.         sourceFile = NXCopyStringBuffer(temp);
  559.     }
  560.   
  561.     /* open , write data, and close */
  562.     [iconMatrix saveToFile:sourceFile];
  563.     [self _setWindowTitle:sourceFile];
  564.  
  565.     return self;
  566. }
  567.  
  568. /* save file names (called by first responder) */
  569. - saveAs:sender
  570. {
  571.     if (sourceFile) { free(sourceFile); sourceFile = (char*)nil; }
  572.     return [self save:sender];
  573. }
  574.  
  575. /* save defaults */
  576. - saveDefaults:sender
  577. {
  578.     char    buff[512];
  579.     NXWriteDefault([NXApp appName], dftFormatNAME, [iconMatrix getPreferenceString:buff]);
  580.     return self;
  581. }
  582.  
  583. // -------------------------------------------------------------------------------------
  584. // calculate window size overhead
  585. // - This method needs to know the layout of the screen to properly calculate its size
  586.  
  587. - getWindowSize:(NXSize*)windowSize forCellSize:(NXSize*)cellSize gap:(NXSize*)gapSize
  588.         rows:(int)rows cols:(int)cols
  589. {
  590.     NXRect    fRect, cRect;
  591.     NXSize    actualSize;
  592.  
  593.     /* calculate actual size of content area */
  594.     actualSize.width  = (cellSize->width  + gapSize->width ) * cols;
  595.     actualSize.height = (cellSize->height + gapSize->height) * rows;
  596.  
  597.     /* calc size of scrollView */
  598.     [[paletteScroll class] getFrameSize:&cRect.size forContentSize:&actualSize
  599.         horizScroller:([paletteScroll horizScroller]?YES:NO)
  600.         vertScroller:([paletteScroll vertScroller]?YES:NO)
  601.         borderType:[paletteScroll borderType]];
  602.  
  603.     /* calc size of window contentView */
  604.     [windowHeader getFrame:&fRect];
  605.       cRect.H += fRect.H;
  606.   
  607.     /* calc window size */
  608.     cRect.X = cRect.Y = 0.0;
  609.     [[paletteWindow class] getFrameRect:&fRect forContentRect:&cRect
  610.           style:[paletteWindow style]];
  611.     *windowSize = fRect.size;
  612.  
  613.     return self;
  614. }
  615.  
  616. /* get current displayed row/col count */
  617. - getDisplayedRows:(int*)rows cols:(int*)cols
  618. {
  619.     NXSize    scSize, *cellSize = [iconMatrix cellSize], *gap = [iconMatrix intercell];
  620.     [paletteScroll getContentSize:&scSize];
  621.     *rows = (int)RINT(scSize.height / (cellSize->height + gap->height));
  622.     *cols = (int)RINT(scSize.width  / (cellSize->width  + gap->width ));
  623.     return self;
  624. }
  625.  
  626. /* return current cell size */
  627. - (NXSize*)cellSize
  628. {
  629.     return [iconMatrix cellSize];
  630. }
  631.  
  632. // -------------------------------------------------------------------------------------
  633. // show selected cell enlarged image (double action from matrix)
  634.  
  635. /* return id to expanded image instance */
  636. - expandImage
  637. {
  638.     if (!expandImage) expandImage = [[ExpandImage alloc] init];
  639.     return expandImage;
  640. }
  641.  
  642. /* matrix doubleAction */
  643. - showLargeImage:sender
  644. {
  645.     id        imageCell = [iconMatrix selectedCell];
  646.     id        expand = [self expandImage];
  647.   
  648.     /* hide large panel and return if no selected cell */
  649.     [expand orderOut:self];
  650.     if (!imageCell) return self;
  651.   
  652.     /* show enlarged image */
  653.     [expand showImage:[imageCell image]:[imageCell imagePath] title:[imageCell cellTitle]];
  654.   
  655.     return self;
  656. }
  657.  
  658. // -------------------------------------------------------------------------------------
  659. // pass file list to workspace manager
  660.  
  661. /* send files to workspace manager for dragging */
  662. - iconDrag:sender
  663. {
  664.     NXRect    bFrame, rect;
  665.     char    *fullPath;
  666.   
  667.     /* ignore if not called by proper button */
  668.     if (sender != iconButtonId) return self;                        // ignore any imposters
  669.   
  670.     /* check for STOP button */
  671.     if (isLOADING) { abortLoad = YES; return self; }                // stop button pressed
  672.   
  673.     /* ignore if no files selected */
  674.     if (!(fullPath = [iconMatrix selectedCellPaths])) return self;    // ignore if no files
  675.   
  676.     /* drag selected files */
  677.     [iconButtonId getBounds:&bFrame];
  678.     rect.W = 48.0;
  679.     rect.H = 48.0;
  680.     rect.X = (bFrame.W - rect.W) / 2.0;
  681.     rect.Y = (bFrame.H - rect.H) / 2.0;
  682.     [self _unregisterWindow];                                        // unregister myself
  683.     [iconButtonId dragFile:fullPath fromRect:&rect slideBack:YES event:[NXApp currentEvent]];
  684.     [self _registerWindow];                                            // re-register myself
  685.   
  686.     /* free file list */
  687.     free(fullPath);
  688.   
  689.     return self;
  690. }
  691.  
  692. // -------------------------------------------------------------------------------------
  693. // PaletteMatrix delegate methods
  694.  
  695. /* (main thread) load file string list */
  696. - loadFileString:(const char*)fileString :(BOOL)openDoc :(BOOL)chkExtn
  697. {
  698.     if (!fileString || !*fileString) return self;
  699.     ParseString ft = fileString;
  700.     return [self loadFileList:&ft :openDoc :chkExtn];
  701. }
  702.  
  703. /* (main thread) load file list table */
  704. - loadFileList:(const ParseString*)fileNames :(BOOL)openDoc :(BOOL)chkExtn
  705. {
  706.     fileLIST    *fl;
  707.   
  708.     /* return if no files to load */
  709.     if (!((void*)fileNames) || !(*fileNames)[0]) return self;
  710.   
  711.     /* allocate and fill file list structure */
  712.     fl = new fileLIST;
  713.     fl->list  = *fileNames;
  714.     fl->flags = (chkExtn?2:0) | (openDoc?1:0);
  715.     fl->next  = (fileLIST*)nil;
  716.   
  717.     /* load images (fork new thread if necessary) */
  718.     mutex_lock(loadMutex);
  719.     if (loadList) loadList->next = fl;        // load already in progress
  720.     else {
  721.         abortLoad = NO;                        // reset abort flag
  722.         [self _setFileIcon:stopIcon];        // setup stop button
  723.         [iconMatrix clearSelectedCell];        // deselect any selected cells
  724.         [self forkPerform:sel_doLoad with:(id)fl detach:YES];
  725.     }
  726.     loadList = fl;
  727.     mutex_unlock(loadMutex);
  728.   
  729.     return self;
  730. }
  731.  
  732. /* set image statistics (only called when a cell is (de)selected) */
  733. - clearImageStats:iconImage        // 'closedFolder' or 'multiFile' only
  734. {
  735.     [iconPathId setStringValue:""];
  736.     [iconSizeId setStringValue:""];
  737.     [self _setFileIcon:iconImage];
  738.     return self;
  739. }
  740.  
  741. /* set image statistics */
  742. - setImageStats:imageCell
  743. {
  744.     NXRect    bBox;
  745.     char    buff[256];
  746.   
  747.     /* clear stats if no cell */
  748.     if (!imageCell) { [self clearImageStats:closedFolder]; return self; }
  749.   
  750.     /* fill info */
  751.     [iconPathId setStringValueNoCopy:[imageCell imagePath]];
  752.     [[imageCell image] getSize:&bBox.size];
  753.     sprintf(buff, "%.0f x %.0f", bBox.W, bBox.H);
  754.     [iconSizeId setStringValue:buff];
  755.   
  756.     /* set icon representation */
  757.     if (fileIcon) [fileIcon free];
  758.     fileIcon = [[Application workspace] getIconForFile:(char*)[imageCell imagePath]];
  759.     [self _setFileIcon:fileIcon];
  760.  
  761.     return self;
  762. }
  763.  
  764. /* image became selected */
  765. - cellBecameSelected:imageCell
  766. {
  767.     if (expandImage) [expandImage orderOut:self];    // hide large panel
  768.     if ([iconMatrix selectedCellCount] <= 1) [self setImageStats:imageCell];
  769.     else [self clearImageStats:multiFile];
  770.     return self;
  771. }
  772.  
  773. /* image became selected */
  774. - cellResignedSelected:imageCell
  775. {
  776.     if ([iconMatrix selectedCellCount] > 1) [self clearImageStats:multiFile];
  777.     else [self setImageStats:[iconMatrix selectedCell]];
  778.     return self;
  779. }
  780.   
  781. // -------------------------------------------------------------------------------------
  782. // Workspace manager icon dragging support
  783. // Note: The static variables declared here are acceptable since only one drag operation
  784. //       can occur at any given time.
  785.  
  786. /* register window with workspace manager */
  787. - _registerWindow
  788. {
  789.     if (!allowDrop || isRegistered) return self;
  790.     [paletteWindow registerForDraggedTypes:pbTypes count:pbNumTypes];
  791.     isRegistered = YES;
  792.     return self;
  793. }
  794.  
  795. /* unregister window with workspace manager */
  796. - _unregisterWindow
  797. {
  798.     if (!allowDrop || !isRegistered) return self;
  799.     [paletteWindow unregisterDraggedTypes];
  800.     isRegistered = NO;
  801.     return self;
  802. }
  803.  
  804. /* file icon entered */
  805. - (NXDragOperation)draggingEntered:sender
  806. {
  807.     Pasteboard    *pb = [sender draggingPasteboard];
  808.     pbDragFilesOk = YES;
  809.     if (![pb findAvailableTypeFrom:pbTypes num:pbNumTypes]) pbDragFilesOk = NO; else
  810.     if (pbChangeCount == [pb changeCount])                  pbDragFilesOk = NO;
  811.     if (pbDragFilesOk) { [iconButtonId setImage:openFolder]; return NX_DragOperationCopy; }
  812.     return NX_DragOperationNone;
  813. }
  814.  
  815. /* file icon moved to location */
  816. - (NXDragOperation)draggingUpdated:sender
  817. {
  818.     return pbDragFilesOk? NX_DragOperationCopy : NX_DragOperationNone;
  819. }
  820.  
  821. /* file icon exited */
  822. // - reset the open file folder icon
  823. - draggingExited:sender
  824. {
  825.     [self _setFileIcon:(id)nil];
  826.     pbDragFilesOk = NO;
  827.     return self;
  828. }
  829.  
  830. /* indicate we accept dragging */
  831. - (BOOL)prepareForDragOperation:sender
  832. {
  833.     return pbDragFilesOk? YES : NO;
  834. }
  835.  
  836. /* files dropped */
  837. - (BOOL)performDragOperation:sender
  838. {
  839.     char        *fileList;
  840.     int            fileListLen;
  841.     Pasteboard    *pb = [sender draggingPasteboard];
  842.     [self _setFileIcon:(id)nil];
  843.     if (pbDragFilesOk && [pb readType:pbTypes[0] data:&fileList length:&fileListLen]) {
  844.         ParseString dragFiles = fileList;
  845.         [pb deallocatePasteboardData:fileList length:fileListLen];
  846.         pbChangeCount = [pb changeCount];
  847.         pbDragFilesOk = NO;
  848.         if (dragFiles[0]) {
  849.             [NXApp activateSelf:YES];
  850.             [paletteWindow makeKeyAndOrderFront:(id)nil];
  851.             [self loadFileList:&dragFiles:NO:(isSHIFT(NX_CONTROLMASK)?NO:YES)];
  852.             return YES;
  853.         }
  854.     }
  855.     return NO;
  856. }
  857.  
  858. /* clean up */
  859. - concludeDragOperation:sender
  860. {
  861.     return self;
  862. }
  863.  
  864. // -------------------------------------------------------------------------------------
  865. // window delegate methods
  866.  
  867. /* window is resizing */
  868. - windowWillResize:windowId toSize:(NXSize*)newSize
  869. {
  870.     float        rows, cols, mRow, mCol;
  871.     NXSize        size, minSize, oneCellSize;
  872.     NXSize        *cellSize = [iconMatrix cellSize], *gap = [iconMatrix intercell];
  873.   
  874.     /* actual cell size */
  875.     oneCellSize = *cellSize;
  876.     oneCellSize.width  += gap->width ;
  877.     oneCellSize.height += gap->height;
  878.  
  879.     /* absolute minimum size */
  880.     if (newSize->width  < minWindowSize.width ) newSize->width  = minWindowSize.width ;
  881.     if (newSize->height < minWindowSize.height) newSize->height = minWindowSize.height;
  882.  
  883.     /* allow any window size if Alternate is pressed */
  884.     if (isSHIFT(NX_ALTERNATEMASK)) {
  885.         if (isLOADING) {    // retain original size
  886.             NXRect wFrame;
  887.             [windowId getFrame:&wFrame];
  888.             *newSize = wFrame.size;
  889.             return self;
  890.         }
  891.         [paletteScroll getContentSize:&size];
  892.         cols = size.width  / oneCellSize.width ;
  893.         rows = size.height / oneCellSize.height;
  894.         minSize.width  = RINT((minCellSize.width +gap->width )*cols)+windowOverhead.width ;
  895.         minSize.height = RINT((minCellSize.height+gap->height)*rows)+windowOverhead.height;
  896.         if (newSize->width  < minSize.width ) newSize->width  = minSize.width;
  897.         if (newSize->height < minSize.height) newSize->height = minSize.height;
  898.         return self;
  899.     }
  900.   
  901.     /* window size must hold at least one cell */
  902.     minSize.width  = windowOverhead.width  + oneCellSize.width ;
  903.     minSize.height = windowOverhead.height + oneCellSize.height;
  904.     if (newSize->width  < minSize.width ) newSize->width  = minSize.width ;
  905.     if (newSize->height < minSize.height) newSize->height = minSize.height;
  906.  
  907.     /* size window to cell boundary */
  908.     cols = RINT((newSize->width  - windowOverhead.width ) / oneCellSize.width );
  909.     rows = RINT((newSize->height - windowOverhead.height) / oneCellSize.height);
  910.     mCol = ceil((minWindowSize.width  - windowOverhead.width ) / oneCellSize.width );
  911.     mRow = ceil((minWindowSize.height - windowOverhead.height) / oneCellSize.height);
  912.     if (cols < mCol) cols = mCol;
  913.     if (rows < mRow) rows = mRow;
  914.     newSize->width  = windowOverhead.width  + cols * oneCellSize.width ;
  915.     newSize->height = windowOverhead.height + rows * oneCellSize.height;
  916.  
  917.     return self;
  918. }
  919.  
  920. /* window is resizing */
  921. - windowDidResize:windowId
  922. {
  923.     float        oldRows, oldCols;
  924.     NXRect    sFrame, matFrame, winFrame;
  925.     NXSize    scSize, *cellSize = [iconMatrix cellSize], *gap = [iconMatrix intercell];
  926.  
  927.     /* ignore if loading is in progress */
  928.     if (isSHIFT(NX_ALTERNATEMASK) && isLOADING) return self;
  929.  
  930.     /* get new window size */
  931.     [[windowId contentView] getFrame:&winFrame];
  932.  
  933.     /* reposition header */
  934.     [windowHeader getFrame:&sFrame];
  935.     winFrame.H -= sFrame.H;
  936.     sFrame.Y = winFrame.H;
  937.     sFrame.W = winFrame.W;
  938.     [windowHeader setFrame:&sFrame];
  939.     headerSize = sFrame.size;
  940.   
  941.     /* resize iconPathId text field */
  942.     [iconPathId getFrame:&sFrame];
  943.     sFrame.W = headerSize.width - sFrame.X;
  944.     [iconPathId setFrame:&sFrame];
  945.  
  946.     /* save current number of displayed rows/cols */
  947.     [paletteScroll getContentSize:&scSize];
  948.     oldCols = scSize.width  / (cellSize->width  + gap->width );
  949.     oldRows = scSize.height / (cellSize->height + gap->height);
  950.   
  951.     /* resize scroller */
  952.     [paletteScroll getFrame:&sFrame];
  953.     sFrame.size = winFrame.size;
  954.     sFrame.Y = 0.0;
  955.     sFrame.X = 0.0;
  956.     [paletteScroll setFrame:&sFrame];
  957.   
  958.     /* resize matrix within scroller */
  959.     [paletteScroll getContentSize:&scSize];
  960.     [iconMatrix getFrame:&matFrame];
  961.     matFrame.size = scSize;
  962.     [iconMatrix setFrame:&matFrame];
  963.  
  964.     /* if alternate key is pressed, then resize cells */
  965.     if (isSHIFT(NX_ALTERNATEMASK)) {
  966.         NXSize    size;
  967.         size.width  = RINT(matFrame.W / oldCols) - gap->width;
  968.         size.height = RINT(matFrame.H / oldRows) - gap->height;
  969.         [iconMatrix setCellSize:&size];
  970.     }
  971.   
  972.     /* size matrix view to fit cells */
  973.     [iconMatrix sizeToCells];
  974.   
  975.     return self;
  976. }
  977.  
  978. /* window will close */
  979. - windowWillClose:windowId
  980. {
  981.   
  982.     /* delay window close if still loading */
  983.     abortLoad = YES;
  984.     if (isLOADING) {
  985.         [windowId perform:sel_performClose with:self afterDelay:250 cancelPrevious:NO];
  986.         return (id)nil;
  987.     }
  988.   
  989.     /* check for saved */
  990.     if ([windowId isDocEdited]) {
  991.         int  rtn;
  992.         const char *save, *no, *cncl, *savf;
  993.           save = NXLocalizedString("Save", (char*)nil,(char*)nil);
  994.           no   = NXLocalizedString("No", (char*)nil,(char*)nil);
  995.           cncl = NXLocalizedString("Cancel", (char*)nil,(char*)nil);
  996.         if (sourceFile) {
  997.             char name[MAXPATHLEN + 1], *p;
  998.             strcpy(name, XFileNameExtension(sourceFile));
  999.             if (p = rindex(name, '.')) *p = 0;
  1000.             savf = NXLocalizedString("Save changes to %s?", (char*)nil,(char*)nil);
  1001.             rtn = NXRunAlertPanel(save, savf, save, no, cncl, name);
  1002.         } else {
  1003.             savf = NXLocalizedString("Save changes to UNTITLED?", (char*)nil,(char*)nil);
  1004.             rtn = NXRunAlertPanel(save, savf, save, no, cncl);
  1005.         }
  1006.         if (rtn == NX_ALERTOTHER) return (id)nil;                    // Cancel
  1007.         if (rtn == NX_ALERTDEFAULT) [self save:(id)nil];            // Save (else No)
  1008.     }
  1009.  
  1010.     /* unregister / remove / and free */
  1011.     [self _unregisterWindow];
  1012.     [instanceList removeObject:self];
  1013.     return [NXApp delayedFree:self];
  1014.   
  1015. }
  1016.  
  1017. /* window became key */
  1018. - windowDidBecomeKey:windowId
  1019. {
  1020.     [[self class] makeActivePortfolio:self];
  1021.     [windowId makeFirstResponder:iconMatrix];
  1022.     [[FontManager new] setSelFont:[iconMatrix font] isMultiple:NO];
  1023.     return self;
  1024. }
  1025.  
  1026. @end
  1027.